package app.services;

import app.Const;
import app.constant.DictConstant;
import app.constant.TransferConstant;
import app.dtos.PayDto;
import app.kit.SmsKit;
import app.kit.TypeKit;
import app.models.basic.Msgs;
import app.models.member.Account;
import app.models.member.AccountAmount;
import app.models.member.Member;
import app.models.member.MemberProduct;
import app.models.order.Order;
import app.models.order.TradeMoney;
import app.models.transfer.TransferProduct;
import app.models.transfer.TransferRule;
import app.models.transfer.TransferTrade;
import app.services.bus.BusContext;
import bank.BankService;
import bank.resp.TransferMerchantRspDto;
import com.google.common.base.Optional;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import goja.Logger;
import goja.StringPool;
import org.joda.time.DateTime;
import org.joda.time.Days;

import java.math.BigDecimal;
import java.sql.SQLException;

import static app.Const.FIELD_AMOUNT;
import static app.Const.FIELD_CODE;
import static app.Const.FIELD_COLLECTION_MODE;
import static app.Const.FIELD_MEMBER;
import static app.Const.FIELD_NAME;
import static app.Const.FIELD_ORDER_ID;
import static app.Const.FIELD_PRODUCT;
import static app.Const.FIELD_STATUS;
import static app.Const.FIELD_TYPE;
import static app.Const.FIELD_YIELD;
import static app.constant.MemberConstant.PLAYMONEY_OK;
import static app.constant.MemberConstant.TRANSFER_TRADE_SUCCESS;
import static app.constant.OrderConstant.TRADE_FAILURE;
import static app.constant.OrderConstant.TRADE_SUCCESS;
import static app.models.member.MemberProduct.FIELD_PLAYMONEY_STATUS;
import static app.models.member.MemberProduct.FIELD_PRODUCT_STATUS;
import static app.models.member.MemberProduct.FIELD_TRANSFER_STATUS;
import static app.models.member.MemberProduct.buildTransferProduct;
import static app.models.member.MemberProduct.dao;
import static app.models.order.TradeMoney.SUCCESS_STATUS;
import static app.models.order.TradeMoney.TRANSFER_TYPE;
import static goja.StringPool.PK_COLUMN;

/**
 * <p> </p>
 *
 * @author sogYF
 * @version 1.0
 * @since JDK 1.6
 */
public final class TransferService {
    private TransferService() {
    }

    public static final TransferService me = new TransferService();


    /**
     * 转让服务
     *
     * @param payDto   转让
     * @param payOrder 支付订单
     */
    public boolean transfer(final PayDto payDto, final Order payOrder, final TransferProduct transferProduct) {

        // 转让产品ID
        long transferProductId = TypeKit.getLong(transferProduct, StringPool.PK_COLUMN);

        // 产品ID
        final int productId = TypeKit.getInt(payOrder, FIELD_PRODUCT);
        // 会员ID
        final int memberId = TypeKit.getInt(payOrder, FIELD_MEMBER);
        // 原始转让产品的价格（ 转让资产）
        final BigDecimal assets = transferProduct.getBigDecimal(FIELD_AMOUNT);
        final int collection_mode = TypeKit.getInt(transferProduct, FIELD_COLLECTION_MODE);
        final float yield = TypeKit.getFloat(transferProduct, FIELD_YIELD);
        // 支付金额
        final BigDecimal payAmount = payDto.totalAmount;
        // 支付日期
        final DateTime payDatetime = payDto.respBizDate;

        // 取得转让方信息
        int member_product_src_id = TypeKit.getInt(transferProduct, "member_product");
        // 转让产品
        final MemberProduct assignorMemberProduct = dao.findById(member_product_src_id);
        // 构建会员转让产品
        final MemberProduct memberProduct = buildTransferProduct(memberId, productId,
                assets, payAmount, payDatetime, collection_mode, yield, transferProductId);
        // 设置产品状态
        memberProduct.set(MemberProduct.FIELD_PRODUCT_STATUS, TypeKit.getInt(assignorMemberProduct, MemberProduct.FIELD_PRODUCT_STATUS));

        DateTime startDate;

        switch (collection_mode) {
            case DictConstant.PROFIT_ROLL:
                // 本息滚动投资，宝宝类产品
                startDate = DateTime.now();
                // 本息滚动投资在到期时间，只有金额为0，那么表示为空
                //  期限0 表示 永不过期
                memberProduct.set("term", 0);
                memberProduct.set("valuedate", startDate);
                memberProduct.set("valuedate_year", startDate.getYear());
                memberProduct.set("valuedate_month", startDate.getMonthOfYear());
                memberProduct.set("valuedate_day", startDate.getDayOfMonth());
                break;
            case DictConstant.PROFIT_ONCE: {
                //一次性到期
                // 产品起息日期
                DateTime valuedate = TypeKit.getDateTime(transferProduct, "product_valuedate");
                if (valuedate == null) {
                    Logger.error("Product data: valuedate is empty, can not transfer success");
                    return false;
                }
                // 产品结束日期
                DateTime duedate = TypeKit.getDateTime(transferProduct, "product_duedate");
                if (duedate == null) {
                    Logger.error("Product data: due_date is empty, can not transfer success");
                    return false;
                }
                // 理财期限
                int remaining_days = Days.daysBetween(valuedate, duedate).getDays();
                memberProduct.set("term", remaining_days);
                memberProduct.set("due_time", duedate);
                memberProduct.set("due_time_year", duedate.getYear());
                memberProduct.set("due_time_month", duedate.getMonthOfYear());
                memberProduct.set("due_time_day", duedate.getDayOfMonth());
                memberProduct.set("valuedate", valuedate);
                memberProduct.set("valuedate_year", valuedate.getYear());
                memberProduct.set("valuedate_month", valuedate.getMonthOfYear());
                memberProduct.set("valuedate_day", valuedate.getDayOfMonth());
                break;
            }
            case DictConstant.PROFIT_MONTH: {
                //  计算起息时间
                final DateTime valuedate = DateTime.now();
                // 是否到起息日期,第二天开始计息
                startDate = valuedate.plusDays(1);
                // 到期时间
                DateTime dueDate = TypeKit.getDateTime(transferProduct, "due_time");
                if (dueDate == null) {
                    Logger.error("转让产品{}到期时间有问题，请检查数据!", transferProductId);
                    return false;
                }
                // 剩余天数
                int remaining_days = Days.daysBetween(startDate, dueDate).getDays();
                // 计算到期时间
                memberProduct.set("term", remaining_days);
                memberProduct.set("due_time", dueDate);
                memberProduct.set("due_time_year", dueDate.getYear());
                memberProduct.set("due_time_month", dueDate.getMonthOfYear());
                memberProduct.set("due_time_day", dueDate.getDayOfMonth());
                memberProduct.set("valuedate", startDate);
                memberProduct.set("valuedate_year", startDate.getYear());
                memberProduct.set("valuedate_month", startDate.getMonthOfYear());
                memberProduct.set("valuedate_day", startDate.getDayOfMonth());
            }

        }

        long payOrderId = TypeKit.getLong(payOrder, StringPool.PK_COLUMN);
        memberProduct.set(FIELD_ORDER_ID, payOrderId);
        memberProduct.set(FIELD_PRODUCT_STATUS, TypeKit.getInt(transferProduct, FIELD_PRODUCT_STATUS));

        //  更新购买订单状态
        payOrder.set(FIELD_STATUS, payDto.orderStatus);
        // 以银行返回信息为主
        payOrder.set("trade_amount", payAmount);
        payOrder.set("fee_amonut", payDto.fee);
        payOrder.set("confirm_date", DateTime.now());


        String productName = transferProduct.getStr(FIELD_NAME);
        // 转让成功标记
        transferProduct.set(FIELD_STATUS, TransferConstant.STATUE_SUCCESS);


        final int assignorMemberId = TypeKit.getInt(transferProduct, "assignor");

            /*计算手续费和挂单费,并纪录打款信息*/
        final TransferRule transferRule = TransferRule.dao.findDefault();
        // 扣除手续费和转让挂单费
        float ratio = TypeKit.getFloat(transferRule, "ratio");
        // 免费时间（天）
        int free_days = TypeKit.getInt(transferRule, "free_times");
        // 打款金额
        final TradeMoney tradeMoney = new TradeMoney();
        tradeMoney.set(FIELD_MEMBER, assignorMemberId);
        tradeMoney.set(FIELD_PRODUCT, transferProduct.get(FIELD_PRODUCT));
        tradeMoney.set("member_product", transferProduct.get(PK_COLUMN));
        tradeMoney.set("principal", payAmount);
        tradeMoney.set(Const.FIELD_TYPE, TradeMoney.TRANSFER_TYPE);
        BigDecimal playmoney = payAmount;
        // 手续费
        BigDecimal transfer_fee = null;
        // 挂单费
        BigDecimal lodging_fee = null;
        if (ratio > 0) {
            // 手续费
            transfer_fee = TransferRule.transferFee(payAmount, transferRule);
            tradeMoney.set("fees", transfer_fee);
            // 减去手续费
            playmoney = playmoney.subtract(transfer_fee);
        }
        // 挂单时间
        DateTime put_time = TypeKit.getDateTime(transferProduct, "put_time");
        if (put_time == null) {
            Logger.error("产品{}挂单时间不存在，无法进行支付", transferProductId);
            return false;
        }
        // 挂单天数
        final int days = Days.daysBetween(put_time, payDatetime).getDays();
        if (days > (free_days - 1)) {
            // 如果挂单时间超过免费时间，则收取挂单费
            BigDecimal lodging_fee_rule = transferRule.getBigDecimal("lodging_fee");
            lodging_fee = lodging_fee_rule.multiply(new BigDecimal(days - (free_days - 1)));
            tradeMoney.set("lodging_fee", lodging_fee);
            playmoney = playmoney.subtract(lodging_fee);
        }
        tradeMoney.set(FIELD_AMOUNT, playmoney);
        tradeMoney.set(FIELD_TYPE, TRANSFER_TYPE);
        tradeMoney.setTime(payDatetime);

        // 转让会员
        final Member assignorMember = Member.dao.findById(assignorMemberId);
        // 转让方账户
        final Account assignorAccount = AccountService.me.transferSaleAccount(playmoney, assets, assignorMemberId);

        // 购买人账号
        final Account payAccount = AccountService.me.transferAccount(playmoney, payAmount, memberId);
        /* 纪录购买人 账户资金变化 */

        // 记录资金记录
        final AccountAmount payAccountAmount = AccountAmount.dao.transfer_expense(memberId, productName, payDatetime, payAmount);
        // 纪录转让方资金纪录
        final AccountAmount assignorAccountAmount = AccountAmount.dao.earning(assignorMemberId, "转让产品：[" + productName + "]，交易成功", payDatetime, playmoney);


        payOrder.set("fee_amonut", transfer_fee == null ? BigDecimal.ZERO : transfer_fee);
        payOrder.set("lodging_amount", lodging_fee == null ? BigDecimal.ZERO : lodging_fee);
        // 处理即时打款问题
        final Order transferOrder = OrderService.me.createTfrPayOrder(assignorMemberId, transferProduct, transfer_fee, lodging_fee, payOrder.getStr("trade_no"));
        transferOrder.set("trade_amount", playmoney);
        final Optional<TransferMerchantRspDto> transferMerchantAccnt = BankService.me.transferMerchantAccnt(DateTime.now(), payDatetime,
                assignorMember.getStr(FIELD_CODE), transferOrder.getStr("trade_no"),
                playmoney, "购买转让产品［" + productName + "］交易即时打款");
        //  找到转让产品的挂单订单
        long pushOrderId = TypeKit.getLong(transferProduct, Const.FIELD_ORDER_ID);
        final Order pushOrder = Order.dao.findById(pushOrderId);
        pushOrder.set(FIELD_STATUS, TRADE_SUCCESS);

        // 转让交易
        final TransferTrade transferTrade = TransferTrade.create(transferProductId, productId, assignorMemberId,
                memberId, payAmount, payDatetime);
        transferTrade.set("lodging_hours", days );
        transferTrade.set("lodging_fee", lodging_fee == null ? BigDecimal.ZERO : lodging_fee);
        // 转让交易 纪录 购买订单
        transferTrade.set(FIELD_ORDER_ID, payOrderId);

        if (transferMerchantAccnt.isPresent() && transferMerchantAccnt.get().isSuccess()) {

            //转让打款成功
            // 支付成功
            transferOrder.set(FIELD_STATUS, TRADE_SUCCESS);
            // 打款状态成功
            tradeMoney.set(FIELD_STATUS, SUCCESS_STATUS);

            // 设置 转让方产品 打款成功
            assignorMemberProduct.set(FIELD_PLAYMONEY_STATUS, PLAYMONEY_OK);
            //  设置转让成功
            assignorMemberProduct.set(FIELD_TRANSFER_STATUS, TRANSFER_TRADE_SUCCESS);
            final Msgs msgs = Msgs.sendMsg(assignorMemberId, "转让交易成功", "温馨提示：您的转让产品[" + productName + "]交易成功。");

            boolean invokeStatus = Db.tx(new IAtom() {
                @Override
                public boolean run() throws SQLException {
                    if (transferOrder.save()) {

                        Number orderId = transferOrder.getNumber(PK_COLUMN);
                        tradeMoney.set(FIELD_ORDER_ID, orderId);
                        return memberProduct.save()
                                && payOrder.update()
                                && payAccount.update()
                                && payAccountAmount.save()
                                && assignorAccount.update()
                                && assignorAccountAmount.save()
                                && transferTrade.save()
                                && tradeMoney.save()
                                && assignorMemberProduct.update()
                                && transferProduct.update()
                                && pushOrder.update()
                                && msgs.save();
                    }
                    return false;
                }
            });
            if (invokeStatus) {
                // 发送短信
                SmsKit.tradeSuccess(assignorMember.getStr("phone"), productName);

                // 提现操作
                if (transfer_fee != null || lodging_fee != null) {
                    BigDecimal withdrawalAmount = (transfer_fee == null ? BigDecimal.ZERO : transfer_fee).add((lodging_fee == null ? BigDecimal.ZERO : lodging_fee));
                    BusContext.postWithdrawals(withdrawalAmount, "转让产品交易成功[" + productName + "]交易手续费及挂单费提现");
                }

            }
            return invokeStatus;
        } else {

            // 打款失败，则不处理转让方的资金纪录，待打款确认后在处理
            final Msgs msgs = Msgs.sendMsg(assignorMemberId, "转让打款失败", "十分抱歉：产品[" + productName + "]转让交易打款失败，平台会在24小时内打款到您的账户中");

            // 打款状态失败
            tradeMoney.set(FIELD_TYPE, TradeMoney.FAIL_STATUS);
            // 转让交易失败
            transferOrder.set(FIELD_STATUS, TRADE_FAILURE);
            return Db.tx(new IAtom() {
                @Override
                public boolean run() throws SQLException {
                    if (transferOrder.save()) {

                        Number orderId = transferOrder.getNumber(PK_COLUMN);
                        tradeMoney.set(FIELD_ORDER_ID, orderId);
                        return memberProduct.save()
                                && payOrder.update()
                                && payAccount.update()
                                && payAccountAmount.update()
                                && assignorAccount.update()
                                && pushOrder.update()
                                && assignorAccountAmount.save()
                                && transferTrade.save()
                                && tradeMoney.save()
                                && transferProduct.update()
                                && msgs.save();
                    }
                    return false;
                }
            });
        }


    }

}
